package com.dyava.dycache.simple_redis_client;

import com.dyava.dycache.core.CacheConstant;
import com.dyava.dycache.redis.RedisCacheWrap;
import com.dyava.dycache.redis.RedisClient;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

import java.util.HashMap;
import java.util.Map;

/**
 * Created by jjliu on 2020/4/9.
 */
public class SimpleRedisClient implements RedisClient {

    static int TIMEOUT=10000;
    static long LOCK_TIME=10000;
    static long LOCK_LOOP_NUM=5;


    public JedisPool pool;
    String redisClientName;

    public SimpleRedisClient(String redisClientName,String ip,int port,String password,int database) {
        this.redisClientName = redisClientName;
        JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxIdle(2);
        config.setMaxTotal(200);
        pool = new JedisPool(config, ip, port, TIMEOUT, password,database);
    }

    String _encode(String str){
        if(str==null){
            return CacheConstant.NULL;
        }else{
            return str;
        }
    }
    String _decode(String str){
        if(CacheConstant.NULL.equalsIgnoreCase(str)){
            return null;
        }else{
            return str;
        }
    }

    String _toLockKey(String key){
        return new StringBuilder("__SRC_LOCK:").append(redisClientName).append(":").append(key).toString();
    }

    @Override
    public String redisClientName() {
        return redisClientName;
    }

    @Override
    public RedisCacheWrap read(String key) {
        try(
                Jedis jedis = pool.getResource();
                ){
            Map<String,String> map = jedis.hgetAll(key);
            if(map==null || map.size()==0){
                return null;
            }
            String data = _decode(map.get(RedisCacheWrap.Fields.data));
            long expireAt = Long.parseLong(map.get(RedisCacheWrap.Fields.expireAt));
            String version = _decode(map.get(RedisCacheWrap.Fields.version));
            return new RedisCacheWrap(data,expireAt,version);
        }
    }

    @Override
    public String readVersion(String key) {
        try(
                Jedis jedis = pool.getResource();
        ){
            String version =jedis.hget(key,RedisCacheWrap.Fields.version);
            return version;
        }
    }

    @Override
    public void write(String key, RedisCacheWrap redisCacheWrap) {
        try(
                Jedis jedis = pool.getResource();
        ){
            Map<String,String> map = new HashMap<>();
            map.put(RedisCacheWrap.Fields.data,_encode(redisCacheWrap.getData()));
            map.put(RedisCacheWrap.Fields.expireAt,redisCacheWrap.getExpireAt()+"");
            map.put(RedisCacheWrap.Fields.version,_encode(redisCacheWrap.getVersion()));
            jedis.hmset(key,map);
            jedis.pexpireAt(key,redisCacheWrap.getExpireAt()+LOCK_TIME);
        }
    }

    @Override
    public void delete(String key) {
        try(
                Jedis jedis = pool.getResource()
        ){
            jedis.hset(key,RedisCacheWrap.Fields.expireAt,System.currentTimeMillis()+"");
            jedis.pexpireAt(key,System.currentTimeMillis()+LOCK_TIME);
        }
    }

    @Override
    public boolean tryLock(String key) {
        try(
                Jedis jedis = pool.getResource();
        ){
            String res = jedis.set(_toLockKey(key),"1","NX","PX",LOCK_TIME);
            if("OK".equalsIgnoreCase(res)){
                return true;
            }else{
                return false;
            }
        }
    }

    @Override
    public boolean lock(String key) {
        for(int i=0;i<LOCK_LOOP_NUM;i++){
            boolean res = tryLock(key);
            if(res){
                return true;
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
            }
        }
        return false;
    }

    @Override
    public void unLock(String key) {
        try(
                Jedis jedis = pool.getResource()
        ){
            jedis.del(_toLockKey(key));
        }
    }
}
